package whf.framework.dao.hibernate;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;

import whf.framework.dao.DAOSupport;
import whf.framework.dao.DaoHelper;
import whf.framework.exception.FindException;
import whf.framework.log.Log;
import whf.framework.log.LogFactory;
import whf.framework.util.BeanInitialization;
import whf.framework.util.StringUtils;
import whf.framework.util.Utils;

/**
 * @author wanghaifeng
 *
 */
public class HibernateDaoSupport<T> extends org.springframework.orm.hibernate3.support.HibernateDaoSupport implements DAOSupport<T> {
	private static Log log = LogFactory.getLog(HibernateDaoSupport.class);
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#save(whf.framework.entity.T)
	 */
	public void save(T entity){
		super.getHibernateTemplate().save(entity);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#load(java.lang.Class, java.io.Serializable)
	 */
	public T load(Class<T> clazz, Serializable key){
		return (T)super.getHibernateTemplate().load(clazz, key);
	}
	
	private Session openSession(){
		//return super.getSessionFactory().openSession();
		return super.getSession();
	}
	
	private void closeSession(Session session){
		//session.close();
		super.releaseSession(session);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#loadWithoutLazy(java.lang.Class, java.io.Serializable)
	 */
	public T loadWithoutLazy(Class<T> clazz, Serializable key){
		Session session = this.openSession();
		try{
			Object obj = session.load(clazz, key);
			BeanInitialization.initializeBean(obj);
			return (T)obj;
		}finally{
			this.closeSession(session);
		}
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#modify(whf.framework.entity.T)
	 */
	public void modify(T entity){
		super.getHibernateTemplate().update(entity);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#delete(whf.framework.entity.T)
	 */
	public void delete(T entity){
		super.getHibernateTemplate().delete(entity);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithoutLazy(java.lang.String)
	 */
	public List<T> findWithoutLazy(String queryString){
		return this.findWithoutLazy(queryString, null);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithoutLazy(java.lang.String, int, int)
	 */
	public List<T> findWithoutLazy(String queryString, int start, int pageSize){
		return this.findWithoutLazy(queryString, null, start, pageSize);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithoutLazy(java.lang.String, java.lang.Object)
	 */
	public List<T> findWithoutLazy(String queryString, Object arg){
		return this.findWithoutLazy(queryString, new Object[]{arg});
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithoutLazy(java.lang.String, java.lang.Object, int, int)
	 */
	public List<T> findWithoutLazy(String queryString, Object arg, int start, int pageSize){
		return this.findWithoutLazy(queryString, new Object[]{arg}, start, pageSize);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithoutLazy(java.lang.String, java.lang.Object[])
	 */
	public List<T> findWithoutLazy(String queryString, Object[] params){
		return this.findWithoutLazy(queryString, params, 0, Integer.MAX_VALUE);
	}
	
	/**
	 * @modify wanghaifeng Aug 25, 2006 1:10:20 PM
	 * @param queryString
	 * @param params
	 * @param start
	 * @param pageSize
	 * @return
	 */
	public List<T> findWithoutLazy(String queryString, Object[] params, int start, int pageSize){
		Session session = this.openSession();
		try{
			Query query = session.createQuery(queryString);
			for(int i=0;params!= null && i< params.length; i++){
				query.setParameter(i, params[i]);
			}
			query.setFirstResult(start);
			query.setMaxResults(pageSize);
			List result = query.list();
			for(Iterator it = result.iterator(); it.hasNext(); ){
				BeanInitialization.initializeBean(it.next());
			}
			return convertList(result);
		}finally{
			this.closeSession(session);
		}
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithListResult(java.lang.String)
	 */
	public List<T> findWithListResult(String query){
		List list = super.getHibernateTemplate().find(query);
		return convertList(list);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithListResult(java.lang.String, java.lang.Object)
	 */
	public List<T> findWithListResult(String query, Object arg){
		List list = super.getHibernateTemplate().find(query,arg);
		return convertList(list);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findWithListResult(java.lang.String, java.lang.Object[])
	 */
	public List<T> findWithListResult(String query, Object[] args){
		List list =  super.getHibernateTemplate().find(query,args);
		return convertList(list);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#list(java.lang.String, int, int)
	 */
	public List<T> list(String queryString, int start, int rowsPerPage) {
		List list = this.getHibernateTemplate().executeFind(
				new InnerHibernateCallback(queryString, start, rowsPerPage) {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						Query query = session.createQuery(getQueryString());
						query.setFirstResult(this.getStart());
						query.setMaxResults(this.getRowsPerPage());
						return query.list();
					}
				});
		return convertList(list);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#list(java.lang.String, java.lang.Object[], int, int)
	 */
	public List<T> list(String queryString, Object[] paramValues, int start,
			int rowsPerPage) {
		List list = this.getHibernateTemplate().executeFind(
				new InnerHibernateCallback(queryString, paramValues, start,
						rowsPerPage) {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						Query query = session.createQuery(getQueryString());
						Object[] paramValues = getParamValues();
						for (int i = 0; paramValues != null
								&& i < paramValues.length; i++) {
							if (paramValues[i] instanceof Date) {
								query.setDate(i, (Date) paramValues[i]);
							} else if (paramValues[i] instanceof Calendar) {
								query.setCalendar(i, (Calendar) paramValues[i]);
							} else {
								query.setParameter(i, paramValues[i]);
							}
						}
						query.setFirstResult(this.getStart());
						query.setMaxResults(this.getRowsPerPage());
						return query.list();
					}
				});
		return convertList(list);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#list(java.lang.String, java.lang.String[], java.lang.Object[], int, int)
	 */
	public List<T> list(String queryString, String[] paramNames,
			Object[] paramValues, int start, int rowsPerPage) {
		List list = this.getHibernateTemplate().executeFind(
				new InnerHibernateCallback(queryString, paramNames,
						paramValues, start, rowsPerPage) {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						Query query = session.createQuery(getQueryString());
						Object[] paramValues = getParamValues();
						String[] paramNames = getParamNames();
						for (int i = 0; paramNames != null
								&& i < paramNames.length; i++) {
							if (paramValues[i] instanceof Date) {
								query.setDate(paramNames[i],
										(Date) paramValues[i]);
							} else if (paramValues[i] instanceof Calendar) {
								query.setCalendar(paramNames[i],
										(Calendar) paramValues[i]);
							} else {
								query.setParameter(paramNames[i],
										paramValues[i]);
							}
						}
						query.setFirstResult(this.getStart());
						query.setMaxResults(this.getRowsPerPage());
						return query.list();
					}
				});
		return convertList(list);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#list(java.lang.String, boolean[], int, int)
	 */
	public List<T> list(String queryString, boolean[] booleanValues, int start,
			int rowsPerPage) {
		List list = this.getHibernateTemplate().executeFind(
				new InnerHibernateCallback(queryString, booleanValues, start,
						rowsPerPage) {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						Query query = session.createQuery(getQueryString());
						boolean[] booleanValues = getBooleanValues();
						for (int i = 0; booleanValues != null
								&& i < booleanValues.length; i++) {
							query.setBoolean(i, booleanValues[i]);
						}
						query.setFirstResult(this.getStart());
						query.setMaxResults(this.getRowsPerPage());
						return query.list();
					}
				});
		return convertList(list);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#queryWithoutLazy(java.lang.Class, java.lang.String, int, int)
	 */
	public List<T> queryWithoutLazy(Class boClass, String queryString, int start,
			int pageSize) throws FindException{
		Session session = this.openSession();
		try{
			Object[] sqlAndParams = DaoHelper.buildSqlForFindByPropertyString(boClass, queryString);
			String sql = sqlAndParams[0].toString();
			Object[] params = (Object[])sqlAndParams[1];
			List<T> list = this.findWithoutLazy(sql, params, start, pageSize);
			for(T entity: list){
				BeanInitialization.initializeBean(entity);
			}
			return list;
		}finally{
			this.closeSession(session);
		}
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#findBySql(java.lang.String, int, int)
	 */
	public List<T> findBySql(Class boClass, String selectFrom, String whereStatement, String orderStatement, int start, int pageSize) throws FindException {
		StringBuilder sb = new StringBuilder();
		if(StringUtils.isEmpty(selectFrom)) throw new IllegalArgumentException("selectFrom is empty!");
		if(selectFrom.toLowerCase().indexOf("select") < 0){
			sb.append("select {t.*} ");
			if(selectFrom.toLowerCase().indexOf("from") < 0){
				sb.append("from ").append(selectFrom).append(" t ");
			}
		} else {
			sb.append(selectFrom);
		}
		if(!StringUtils.isEmpty(whereStatement)){
			if(whereStatement.toLowerCase().indexOf("where") < 0){
				sb.append(" where ");
			}
			sb.append(whereStatement);
		}
		if(!StringUtils.isEmpty(orderStatement)){
			sb.append(orderStatement);
		}
		log.debug(sb);
		Session session=null;
		List list = null;
		try{
			session=getSession();
			Query query = session.createSQLQuery(sb.toString())
							.addEntity("t", boClass)
							.setFirstResult(start)
							.setMaxResults(pageSize);
			list = query.list();
		} catch (Exception e){
			throw new FindException(e);
		}finally{
			if(session != null) session.close();
		}
		return convertList(list);
	}
	
	public List<T> getNamedQueryResult(String queryName, Object[] params, int start, int pageSize) throws FindException {
		Session session = null;
		try{
			session = getSession();
			Query query = session.getNamedQuery(queryName);
			log.debug(query.getQueryString());
			log.debug(params);
			for(int i=0; i< params.length; i++){
				query.setParameter(i, params[i]);
			}
			List list = query.list();
			for(Object o:list){
				BeanInitialization.initializeBean(o);
			}
			return list;
		} catch(RuntimeException e){
			log.error("getNamedQueryResult error: query name is '" + queryName + "'", e);
			throw e;
		}finally{
			session.close();
		}
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAOSupport#getNamedQueryResult(java.lang.String, java.lang.String[], java.lang.Object[], int, int)
	 */
	public List<T> getNamedQueryResult(String queryName, String[] paramNames, Object[] params, int start, int pageSize) throws FindException {
		Session session = null;
		try{
			session = getSession();
			Query query = session.getNamedQuery(queryName);
			log.debug(query.getQueryString());
			log.debug(params);
			for(int i=0; i< paramNames.length; i++){
				query.setParameter(paramNames[i], params[i]);
			}
			List list = query.list();
			for(Object o:list){
				BeanInitialization.initializeBean(o);
			}
			return list;
		} catch(RuntimeException e){
			log.error("getNamedQueryResult error: query name is '" + queryName + "'", e);
			throw e;
		}finally{
			session.close();
		}
	}

	private List<T> convertList(Collection collection) {
		List<T> list = Utils.newArrayList();
		list.addAll(collection);
		return list;
	}
	
	// ------------------------------
	abstract class InnerHibernateCallback implements HibernateCallback {
		private int start;

		private int rowsPerPage;

		private String queryString;

		private String[] paramNames;

		private Object[] paramValues;

		private boolean[] booleanValues;

		public InnerHibernateCallback(String queryString, int start,
				int rowsPerPage) {
			this.queryString = queryString;
			this.start = start;
			this.rowsPerPage = rowsPerPage;
		}

		public InnerHibernateCallback(String queryString, Object[] paramValues,
				int start, int rowsPerPage) {
			this.queryString = queryString;
			this.paramValues = paramValues;
			this.start = start;
			this.rowsPerPage = rowsPerPage;
		}

		public InnerHibernateCallback(String queryString, String[] paramNames,
				Object[] paramValues, int start, int rowsPerPage) {
			this.queryString = queryString;
			this.paramNames = paramNames;
			this.paramValues = paramValues;
			this.start = start;
			this.rowsPerPage = rowsPerPage;
		}

		public InnerHibernateCallback(String queryString,
				boolean[] booleanValues, int start, int rowsPerPage) {
			this.queryString = queryString;
			this.booleanValues = booleanValues;
			this.start = start;
			this.rowsPerPage = rowsPerPage;
		}

		public int getStart() {
			return this.start;
		}

		public int getRowsPerPage() {
			return this.rowsPerPage;
		}

		public String getQueryString() {
			return this.queryString;
		}

		public Object[] getParamValues() {
			return this.paramValues;
		}

		public String[] getParamNames() {
			return this.paramNames;
		}

		public boolean[] getBooleanValues() {
			return this.booleanValues;
		}

		public abstract Object doInHibernate(Session session)
				throws HibernateException, SQLException;
	}

	
	
}
